home *** CD-ROM | disk | FTP | other *** search
/ TPUG - Toronto PET Users Group / TPUG Users Group CD / TPUG Users Group CD.iso / AMIGA / (A)G / (A)G11.ADF / Maze / maze.c < prev    next >
C/C++ Source or Header  |  1989-08-17  |  37KB  |  1,126 lines

  1. #include <exec/types.h>
  2. #include <intuition/intuition.h>
  3. #include <functions.h>
  4. #include <graphics/gfxmacros.h>
  5. #include <graphics/text.h>
  6. struct IntuitionBase *IntuitionBase;
  7. struct GfxBase       *GfxBase;
  8.  
  9. struct RastPort  tmpRPactual;
  10. struct RastPort *tmpRP = &tmpRPactual;
  11. struct BitMap    tmpBM;
  12.  
  13. struct TextAttr font = {
  14.         (STRPTR)"topaz.font",   /* Font Name */
  15.         TOPAZ_EIGHTY,    /* Font Height */
  16.         FS_NORMAL,      /* Font Style */
  17.         FPF_ROMFONT     /* Preferences */
  18.         };
  19.  
  20. #define tmpBMxy 60L
  21.  
  22. #define windowW 552L
  23. #define windowH 183L
  24.  
  25. struct NewWindow nw = {
  26.       6, 12, windowW, windowH,      /* EDGES, SIZE */
  27.       0, 1,                         /* PENS */
  28.       CLOSEWINDOW,                  /* IDCMPFlags */
  29.       WINDOWDRAG  | WINDOWDEPTH |   /* Flags (requested window features)*/
  30.         WINDOWCLOSE | ACTIVATE | REPORTMOUSE,
  31.       NULL,                         /* gadgets */
  32.       NULL,                         /* checkmark image */
  33.       NULL,                         /* title */
  34.       NULL,                         /* screen */
  35.       NULL,                         /* bitmap */
  36.       0,0,0,0,                      /* min and max w and h */
  37.       WBENCHSCREEN,                 /* TYPE */
  38.       };
  39.  
  40.  
  41. struct IntuiText it_GiveUp = {
  42.         0,1,        /* frontpen, backpen */
  43.         JAM1,       /* drawmode */
  44.         1,1,        /* leftedge, topedge */
  45.         &font,       /* TextAttr * ITextFont */
  46.         (UBYTE *)" Give Up! ",  /* IText */
  47.         NULL,       /* NextText */
  48.         };
  49. struct MenuItem mi_GiveUp = {
  50.         (struct MenuItem *) NULL,
  51.         0,40,                        /* LeftEdge, TopEdge */
  52.         21*9,10,                    /* Width, Height */
  53.         ITEMTEXT | HIGHCOMP | ITEMENABLED,        /* Flags */
  54.         0,                          /* Mutual Exclude */
  55.         (APTR)&it_GiveUp,           /* ItemFill */
  56.         NULL,                       /* SelectFill */
  57.         0,                          /* Command    */
  58.         NULL,                       /* Subitem */
  59.         0,                          /* NextSelect */
  60.         };
  61.  
  62. struct IntuiText it_3Level = {
  63.         0,1,        /* frontpen, backpen */
  64.         JAM1,       /* drawmode */
  65.         1,1,        /* leftedge, topedge */
  66.         &font,       /* TextAttr * ITextFont */
  67.         (UBYTE *)"New 3-Level Maze",  /* IText */
  68.         NULL,       /* NextText */
  69.         };
  70. struct MenuItem mi_3Level = {
  71.         (struct MenuItem *) &mi_GiveUp,
  72.         0,30,                        /* LeftEdge, TopEdge */
  73.         21*9,10,                    /* Width, Height */
  74.         ITEMTEXT | HIGHCOMP | ITEMENABLED,        /* Flags */
  75.         0,                          /* Mutual Exclude */
  76.         (APTR)&it_3Level,           /* ItemFill */
  77.         NULL,                       /* SelectFill */
  78.         0,                          /* Command    */
  79.         NULL,                       /* Subitem */
  80.         0,                          /* NextSelect */
  81.         };
  82.  
  83. struct IntuiText it_2Level = {
  84.         0,1,        /* frontpen, backpen */
  85.         JAM1,       /* drawmode */
  86.         1,1,        /* leftedge, topedge */
  87.         &font,       /* TextAttr * ITextFont */
  88.         (UBYTE *)"New 2-Level Maze",  /* IText */
  89.         NULL,       /* NextText */
  90.         };
  91. struct MenuItem mi_2Level = {
  92.         (struct MenuItem *) &mi_3Level,
  93.         0,20,                      /* LeftEdge, TopEdge */
  94.         21*9,10,                    /* Width, Height */
  95.         ITEMTEXT | HIGHCOMP | ITEMENABLED,        /* Flags */
  96.         0,                          /* Mutual Exclude */
  97.         (APTR)&it_2Level,           /* ItemFill */
  98.         NULL,                       /* SelectFill */
  99.         0,                          /* Command    */
  100.         NULL,                       /* Subitem */
  101.         0,                          /* NextSelect */
  102.         };
  103. struct IntuiText it_1Level = {
  104.         0,1,        /* frontpen, backpen */
  105.         JAM1,       /* drawmode */
  106.         1,1,        /* leftedge, topedge */
  107.         &font,       /* TextAttr * ITextFont */
  108.         (UBYTE *)"New 1-Level Maze",  /* IText */
  109.         NULL,       /* NextText */
  110.         };
  111. struct MenuItem mi_1Level = {
  112.         (struct MenuItem *)&mi_2Level,
  113.         0,10,                      /* LeftEdge, TopEdge */
  114.         21*9,10,                    /* Width, Height */
  115.         ITEMTEXT | HIGHCOMP | ITEMENABLED,        /* Flags */
  116.         0,                          /* Mutual Exclude */
  117.         (APTR)&it_1Level,            /* ItemFill */
  118.         NULL,                       /* SelectFill */
  119.         0,                          /* Command    */
  120.         NULL,                       /* Subitem */
  121.         0,                          /* NextSelect */
  122.         };
  123.  
  124. struct IntuiText it_About = {
  125.         0,1,        /* frontpen, backpen */
  126.         JAM1,       /* drawmode */
  127.         1,1,        /* leftedge, topedge */
  128.         &font,       /* TextAttr * ITextFont */
  129.         (UBYTE *)"About TML's AmigaMaze",  /* IText */
  130.         NULL,      /* NextText */
  131.         };
  132. struct MenuItem mi_About = {
  133.         (struct MenuItem *) &mi_1Level,
  134.         0,0,                      /* LeftEdge, TopEdge */
  135.         21*9,10,                    /* Width, Height */
  136.         ITEMTEXT | HIGHCOMP | ITEMENABLED,        /* Flags */
  137.         0,                          /* Mutual Exclude */
  138.         (APTR)&it_About,            /* ItemFill */
  139.         NULL,                       /* SelectFill */
  140.         0,                          /* Command    */
  141.         NULL,                       /* Subitem */
  142.         0,                         /* NextSelect */
  143.         };
  144. struct Menu menu = {
  145.         (struct Menu *) NULL,    /* NEXT menu */
  146.         0, 0, 12*9, 0,           /* LeftEdge, TopEdge, Width, Height */
  147.         MENUENABLED,             /* Flags */
  148.         (BYTE *) "Maze Control", /* MenuName */
  149.         (struct MenuItem *)&mi_About,            /* First item */
  150.         0,0,0,0,                /* JazzX,JazzY, BeatX, BeatY */
  151.         };
  152.  
  153. struct Window *window;
  154.  
  155. /* BEWARE: NOWHERE has a dual nature, being used as a doesn't-go-anywhere
  156.    indicator, and also as an invalid level marker!!!   */
  157. #define NOWHERE -1
  158. #define SOUTH    0
  159. #define EAST     1
  160. #define WEST     2
  161. #define NORTH    3
  162.  
  163. #define SOLVED   -3
  164.  
  165. #define DIRECTIONS 4
  166. #define MAXLEVELS  3
  167. #define BOARDMAXX 36
  168. #define BOARDMAXY 29
  169. /*** the following are "inpath" stata. (see struct square) *******/
  170. #define NOTTRAVERSED  1
  171. #define EDGE          2
  172. #define LASTPIECE     3 + 32
  173. #define FIRSTPIECE    3 + 64
  174. #define CURPIECE      3 + 96
  175. #define TRAVERSED     3
  176.  
  177. #define MIDCMP1 CLOSEWINDOW|MOUSEMOVE|MOUSEBUTTONS|MENUPICK|MENUVERIFY
  178. #define MIDCMP2 REQCLEAR
  179. #define MIDCMP3 CLOSEWINDOW
  180.  
  181. extern ULONG RangeRand();
  182. extern int about();
  183. extern int cycle();
  184.  
  185. typedef struct {
  186.        int conlev[ DIRECTIONS ]; /** level connected to in each dir. **/
  187.        int inpath;  /** Also the color this should be drawn in.  **/
  188.        int compass; /** Which direction to go to get home from here **/
  189.        } square;
  190.  
  191. typedef square BOARD[ BOARDMAXX+1 ][ BOARDMAXY+1 ][ MAXLEVELS ];
  192.  
  193. BOARD board;
  194.  
  195. struct RastPort *rp;
  196.  
  197. int StartX, StartY, StartLevel;
  198. int EndX,   EndY,   EndLevel;
  199.  
  200. int CurX,   CurY,   CurLevel;
  201. int NewX,   NewY,   NewLevel;
  202. int MouseX, MouseY;
  203. int MinLevel, MaxLevel;
  204. int BoardMaxX = BOARDMAXX;
  205. int BoardMaxY = BOARDMAXY;
  206. int SquareXsize = 34;
  207. int SquareYsize = 18;
  208.  
  209. /* We need to have some Fill patterns for various places.  Here goes... */
  210. USHORT Pat_Normal[] = {  /** the normal filled pattern **/
  211.        0xffff,  /* plane 1 pattern */
  212.        0xffff,
  213.        0xffff,  /* plane 2 pattern */
  214.        0xffff  };
  215.  
  216. USHORT Pat_P1[] = {  /** first dithered filled pattern **/
  217.        0x5555,  /* plane 1 pattern */
  218.        0xaaaa,
  219.        0xffff,  /* plane 2 pattern */
  220.        0xffff  };
  221.  
  222. USHORT Pat_P2[] = {  /** second dithered filled pattern **/
  223.        0xffff,  /* plane 1 pattern */
  224.        0xffff,
  225.        0x5555,  /* plane 2 pattern */
  226.        0xaaaa   };
  227.  
  228. setHint(ShowHint)
  229. int ShowHint;
  230. {
  231.     char *s;
  232.     int  oldBPen;
  233.  
  234.     if (ShowHint) {
  235.        switch ( board[CurX][CurY][CurLevel].compass) {
  236.           case NORTH :   s = " HINT: Go North!                "; break;
  237.           case SOUTH :   s = " HINT: Go South!                "; break;
  238.           case EAST  :   s = " HINT: Go East!                 "; break;
  239.           case WEST  :   s = " HINT: Go West!                 "; break;
  240.           case SOLVED:   s = " CONGRATULATIONS!               "; break;
  241.           default    :   s = " Hmm.  I don't know.            "; break;
  242.           }
  243.        }
  244.       else {
  245.        s = " (Press Left Button for a hint.)";
  246.        }
  247.     Move(rp, 20L, 180L);
  248.     SetAPen(rp, 1L);
  249.     oldBPen = rp -> BgPen;
  250.     SetBPen(rp, 0L);
  251.     SetDrMd(rp, (LONG)JAM2);
  252.     Text(rp, s, 32L);
  253.     SetBPen(rp, (LONG)oldBPen);
  254.     }
  255.  
  256. showside(ax,ay, bx,by, cx,cy, dx,dy, color)
  257. LONG ax,ay, bx,by, cx,cy, dx,dy, color;
  258. {
  259.     SetAPen(  tmpRP, color);   /* The shape isn't allways a rect., so */
  260.     SetOPen(  tmpRP, color);   /*  we can't use RectFill() here...  */
  261.     BNDRYOFF( tmpRP);          /* Boundaries look bad w/ patterns...*/
  262.     switch (color) {
  263.        case CURPIECE     :
  264.        case TRAVERSED    : SetAfPt( tmpRP, Pat_P2,-1L); break;
  265.        case LASTPIECE    : SetAfPt( tmpRP, Pat_P1,-1L); break;
  266.        case NOTTRAVERSED :
  267.        case FIRSTPIECE   :
  268.        default           : SetAfPt( tmpRP, Pat_Normal,-1L); break;
  269.        }
  270.  
  271.     AreaMove( tmpRP, ax,ay);
  272.     AreaDraw( tmpRP, bx,by);
  273.     AreaDraw( tmpRP, cx,cy);
  274.     AreaDraw( tmpRP, dx,dy);
  275.     AreaEnd(  tmpRP);
  276.     SetAPen(  tmpRP, (LONG)EDGE);
  277.     Move(     tmpRP, ax,  ay);
  278.     Draw(     tmpRP, bx,  by);
  279.     Draw(     tmpRP, bx+1,by);
  280.     Draw(     tmpRP, ax+1,ay);
  281.     Move(     tmpRP, cx,  cy);
  282.     Draw(     tmpRP, dx,  dy);
  283.     Draw(     tmpRP, dx+1,dy);
  284.     Draw(     tmpRP, cx+1,cy);
  285.     }
  286.  
  287. putedge(ax,ay,bx,by)
  288. LONG ax,ay,bx,by;
  289. {
  290.     SetAPen(tmpRP,(LONG)EDGE);
  291.     Move(tmpRP,ax,  ay);
  292.     Draw(tmpRP,bx,  by);
  293.     Draw(tmpRP,bx+1,by);
  294.     Draw(tmpRP,ax+1,ay);
  295.     }
  296.  
  297. LONG renderColor(x, y, z, x1, y1, z1)
  298. int x, y, z, x1, y1, z1;
  299. {   int color;
  300.     color = board[x][y][z].inpath;
  301.     if (!(z == MaxLevel-1 && (color == FIRSTPIECE || color == LASTPIECE)))
  302.        if ((color == NOTTRAVERSED) ||
  303.            (board[x1][y1][z1].inpath == NOTTRAVERSED))
  304.            color = NOTTRAVERSED;
  305.     return((LONG)color);
  306.     }
  307.  
  308. #define XDIF 6
  309. #define YDIF 3
  310.  
  311. render(x,y)
  312. int x, y;
  313. {
  314.    LONG ax,ay, bx,by, cx,cy, dx,dy, color;
  315.    int level, Bleft, Btop, deltaX, deltaY, tmpL;
  316.    int left, right, bottom, top;
  317.  
  318.    /* clear the temporary bitmap */
  319.    SetAPen( tmpRP, 0L);
  320.    SetBPen( tmpRP, 0L);
  321.    RectFill( tmpRP, 0L, 0L, (LONG)SquareXsize, (LONG)SquareYsize );
  322.  
  323.    Bleft  =  4 + (x - 1) * SquareXsize;
  324.    Btop   = 11 + (y - 1) * SquareYsize;
  325.  
  326.    left   = 0;
  327.    right  = SquareXsize - 1;
  328.    top    = 0;
  329.    bottom = SquareYsize - 1;
  330.  
  331.    for (level=MinLevel; level<MaxLevel; level++) {
  332.        if (references(x, y, level) == 0)
  333.           continue; /* This level doesn't go anywhere. */
  334.  
  335.        deltaY = YDIF * level  + 1;
  336.        deltaX = XDIF * level  + 1;
  337.  
  338.        /**** Fill in the middle part first ****/
  339.        ax = right - deltaX;         ay = top + deltaY;
  340.        bx = left  + deltaX;         by = ay;
  341.        cx = bx;                     cy = bottom - deltaY;
  342.        dx = ax;                     dy = cy;
  343.        color = board[x][y][level].inpath;
  344.        showside(ax,ay,bx,by,cx,cy,dx,dy,color);
  345.  
  346.        /**** Do the NORTH side of this level first ****/
  347.        ax = right-deltaX;      ay = top + deltaY;
  348.        bx = ax;                by = top;
  349.        cx = left +deltaX;      cy = by;
  350.        dx = cx;                dy = ay;
  351.        tmpL  = board[x][y][level].conlev[NORTH];
  352.        if ( tmpL == NOWHERE) {
  353.             putedge(ax,ay,dx,dy);
  354.             goto DrawWest;
  355.             }
  356.        color = renderColor(x,y,level,x,y-1,tmpL);
  357.        if (tmpL != level && level == 1) {
  358.             bx = bx - XDIF*(tmpL - 1);
  359.             cx = cx + XDIF*(tmpL - 1);
  360.             }
  361.        showside(ax,ay,bx,by,cx,cy,dx,dy,color);
  362.  
  363.        /**** Do the WEST side of this level next ****/
  364.        DrawWest:
  365.        ax = left + deltaX;      ay = top + deltaY;
  366.        bx = left;               by = ay;
  367.        cx = bx;                 cy = bottom - deltaY;
  368.        dx = ax;                 dy = cy;
  369.        tmpL  = board[x][y][level].conlev[WEST];
  370.        if ( tmpL == NOWHERE) {
  371.             putedge(ax,ay,dx,dy);
  372.             goto DrawSouth;
  373.             }
  374.        color = renderColor(x,y,level,x-1,y,tmpL);
  375.        if (tmpL != level && level == 1) {
  376.             by = by + YDIF*(tmpL - 1);
  377.             cy = cy - YDIF*(tmpL - 1);
  378.             }
  379.        showside(ax,ay,bx,by,cx,cy,dx,dy,color);
  380.  
  381.        /**** Do the SOUTH side of this level next ****/
  382.        DrawSouth:
  383.        ax = left + deltaX;      ay = bottom - deltaY;
  384.        bx = ax;                 by = bottom;
  385.        cx = right - deltaX;     cy = by;
  386.        dx = cx;                 dy = ay;
  387.        tmpL  = board[x][y][level].conlev[SOUTH];
  388.        if ( tmpL == NOWHERE) {
  389.             putedge(ax,ay,dx,dy);
  390.             goto DrawEast;
  391.             }
  392.        color = renderColor(x,y,level,x,y+1,tmpL);
  393.        if (tmpL != level && level == 1) {
  394.             bx = bx + XDIF*(tmpL-1);
  395.             cx = cx - XDIF*(tmpL-1);
  396.             }
  397.        showside(ax,ay,bx,by,cx,cy,dx,dy,color);
  398.  
  399.        /**** Do the EAST side of this level last ****/
  400.        DrawEast:
  401.        ax = right - deltaX;     ay = bottom - deltaY;
  402.        bx = right;              by = ay;
  403.        cx = bx;                 cy = top + deltaY;
  404.        dx = ax;                 dy = cy;
  405.        tmpL  = board[x][y][level].conlev[EAST];
  406.        if ( tmpL == NOWHERE) {
  407.             putedge(ax,ay,dx,dy);
  408.             goto DrawComplete;
  409.             }
  410.        color = renderColor(x,y,level,x+1,y,tmpL);
  411.        if (tmpL != level && level == 1) {
  412.             by = by - YDIF*(tmpL - 1);
  413.             cy = cy + YDIF*(tmpL - 1);
  414.             }
  415.        showside(ax,ay,bx,by,cx,cy,dx,dy,color);
  416.        DrawComplete:;
  417.        }
  418.    /* now copy temporary bitmap into window */
  419.  
  420.    BltBitMapRastPort(&tmpBM,0L,0L, rp,(LONG)Bleft,(LONG)Btop,
  421.             (LONG)SquareXsize,(LONG)SquareYsize, (LONG)0x0c0);
  422.    }  /* end of render() */
  423.  
  424.  
  425. /* references() returns the number connections from a given square. */
  426. int references(x, y, level)
  427. int x, y, level;
  428. { int i, refs;
  429.     refs = 0;
  430.     for (i=0; i < DIRECTIONS; i++)
  431.         if (board[x][y][level].conlev[i] != NOWHERE)
  432.             refs++;
  433.     return(refs);
  434.     }
  435.  
  436. /* linkable() returns true if we can connect the two indicated squares. */
  437. /* NOTE: We don't allow connecting to the second square if it */
  438. /* is already part of the maze.  This test if the first if stmt.*/
  439. /* The second test if more subtle.  The problem being tested for is shown in */
  440. /* the following diagram.  This is an edge-on view of the board: */
  441. /*   a __    __ d   */
  442. /*       \  /       */
  443. /*         /        */
  444. /*        /         */
  445. /*   c __/  \__ b   */
  446. /* If c and d are connected, I can't connect a to b because the paths would */
  447. /* intersect.  The way the test is written, it will work regardless of whether */
  448. /* the new path is going up, down, or level. */
  449.  
  450. int linkable(x1,y1,level1,dir1, x2,y2, level2, dir2)
  451. int x1,y1,level1,dir1, x2,y2,level2;
  452. {
  453.    int tmp;
  454.    if (references(x2,y2,level2)) return(0);
  455.    if (board[x1][y1][level2].conlev[dir1] == level1) return(0);
  456.    return(-1);
  457.    }
  458.  
  459. /* Returns the other Direction, or NOWHERE if an invalid direction is passed. */
  460. int otherDir(nd)
  461. int nd;
  462. { switch (nd) {
  463.     case NORTH : return(SOUTH); break;
  464.     case SOUTH : return(NORTH); break;
  465.     case EAST  : return(WEST) ; break;
  466.     case WEST  : return(EAST) ; break;
  467.     }
  468.   return(NOWHERE);
  469.   }
  470.  
  471.  
  472. /* makepathlist() returns the number of directions we can go from here w/o */
  473. /*  backtracking -- i.e., not counting the way we got here.  These */
  474. /*  "uncharted" routes are characterized by .compus==NOWHERE.  The list of */
  475. /*  directions is placed in pl[]. */
  476.  
  477. int makepathlist(x,y,z,pl)
  478. int x, y, z, pl[];
  479. {   int di, le, paths;
  480.     paths = 0;
  481.     for (di=0; di<DIRECTIONS; di++) {
  482.         pl[paths] = di;
  483.         le = board[x][y][z].conlev[di];
  484.         if (le != NOWHERE)
  485.            switch (di) {
  486.               case NORTH   :  if (board[x][y-1][le].compass == NOWHERE)
  487.                                  paths++;
  488.                               break;
  489.               case SOUTH   :  if (board[x][y+1][le].compass == NOWHERE)
  490.                                  paths++;
  491.                               break;
  492.               case EAST    :  if (board[x+1][y][le].compass == NOWHERE)
  493.                                  paths++;
  494.                               break;
  495.               case WEST    :  if (board[x-1][y][le].compass == NOWHERE)
  496.                                  paths++;
  497.                               break;
  498.               case NOWHERE :  break;
  499.               }
  500.         }
  501.     return(paths);
  502.     }
  503.  
  504. int topscore;
  505.  
  506. /* buildreturnmap() is a recursive routine which starts the end of a path on */
  507. /* our tree-structured maze and traverses up to an intersection or dead end. */
  508. /* When we hit a dead end, we just return, but when we hit an intersection, we */
  509. /* call buildreturnmap() again, once for each path going off from the */
  510. /* intersection, except for the one got there on.  We keep a score for how far */
  511. /* we are from the first square.  One point per square, plus 5 times the */
  512. /* number of paths leading off from each intersection.  We keep a topscore to */
  513. /* find out which square is farthest (in this modified sense) from our */
  514. /* starting square.  This helps to ensure that we get a reasonably difficult */
  515. /* maze to solve. */
  516. /*   We also record the direction to the end of the maze in each square.  This */
  517. /* information is used by the hint facility. */
  518.  
  519. buildreturnmap(x,y,z,score)
  520. int x, y, z, score;
  521. {   int pathlist[DIRECTIONS], paths, i, le;
  522.  
  523.     while ( (paths = makepathlist(x,y,z,pathlist)) == 1) {
  524.           z = board[x][y][z].conlev[pathlist[0]];
  525.           switch (pathlist[0]) {
  526.             case NORTH : y--; break;
  527.             case SOUTH : y++; break;
  528.             case EAST  : x++; break;
  529.             case WEST  : x--; break;
  530.             }
  531.           board[x][y][z].compass = otherDir(pathlist[0]);
  532.           /* score++; */
  533.           }
  534.  
  535.     if (paths == 0) {   /* We are at the end of a path... */
  536.         if (score >= topscore) {
  537.             topscore = score;
  538.             StartX = x;
  539.             StartY = y;
  540.             StartLevel = z;
  541.             }
  542.         return;
  543.         }
  544.  
  545.     /* if we got here, we are not at the end of a path.   paths > 1 */
  546.     score = score + paths;
  547.  
  548.     for (i=0; i<paths; i++) {
  549.         le = board[x][y][z].conlev[pathlist[i]];
  550.         switch (pathlist[i]) {
  551.            case NORTH   : board[x][y-1][le].compass = SOUTH;
  552.                           buildreturnmap(x,y-1,le,score);     break;
  553.  
  554.            case SOUTH   : board[x][y+1][le].compass = NORTH;
  555.                           buildreturnmap(x,y+1,le,score);     break;
  556.  
  557.            case EAST    : board[x+1][y][le].compass = WEST;
  558.                           buildreturnmap(x+1,y,le,score);     break;
  559.  
  560.            case WEST    : board[x-1][y][le].compass = EAST;
  561.                           buildreturnmap(x-1,y,le,score);     break;
  562.            }
  563.         }
  564.     }
  565.  
  566. #define MAXENDS 120
  567. #define FREEEND -1
  568. #define LASTEND -1
  569. typedef struct {
  570.         int x,y,z;    /* board coordinates. x=FREEEND means this one is free */
  571.         int prevdir;  /* The direction we went to get here. */
  572.                       /*    In picking what direction to go, we are biased */
  573.                       /*    toward this direction.  This gives paths */
  574.                       /*    with several straight sections -- keeps one path from */
  575.                       /*    knotting up one part of the board. */
  576.         int nextfree; /* next unused end, LASTEND means no more  */
  577.         } endtype;
  578.  
  579. endtype ends[MAXENDS];
  580. int     firstfree;
  581. int     MaxEnds = MAXENDS;
  582.  
  583. initends()
  584. {   int i;
  585.  
  586.     for (i=0; i<MaxEnds; i++) {
  587.         ends[i].x = -1;
  588.         ends[i].y = -1;
  589.         ends[i].z = -1;
  590.         ends[i].nextfree = i - 1;
  591.         }
  592.     firstfree = MaxEnds - 1;
  593.     /* NOTE that ends[0].nextfree == -1 == LASTEND.  If that define ever */
  594.     /* changes, we will have to include the statement: */
  595.     /* ends[0].nextfree = LASTEND; */
  596.     }
  597.  
  598. /* IF you want to play around with the parameters that effect the way
  599.    boards are built, you will want to try different combinations of
  600.    "tries", "length", and global MaxEnds. */
  601.  
  602. int extendend(curend,tries,length)
  603. int curend, tries, length;
  604. {
  605.     int x, y, level;
  606.     int nd,od, nx, ny, nl, i, moves, link;
  607.  
  608.     moves = 0;
  609.  
  610.     x     = ends[curend].x;
  611.     y     = ends[curend].y;
  612.     level = ends[curend].z;
  613.     nd    = ends[curend].prevdir;
  614.  
  615.     /* try up to i times to add a random square */
  616.     for (i=0; i<tries && moves < length; i++) {
  617.         do {         /*  try not to go out of bounds */
  618.              nd = ((RangeRand(10L) < 4) ? (int)RangeRand((LONG)DIRECTIONS) : nd);
  619.              } while ((nd == NORTH && y == 1)           ||
  620.                       (nd == WEST  && x == 1)           ||
  621.                       (nd == SOUTH && y == BoardMaxY-1) ||
  622.                       (nd == EAST  && x == BoardMaxX-1)     );
  623.         /* Now that we've got a direction, see that we don't already go that way */
  624.         if (board[x][y][level].conlev[nd] != NOWHERE)
  625.             continue;
  626.         nx = x;  ny = y;
  627.         switch (nd) {                /* work out the new x and y */
  628.             case NORTH : ny = y - 1; break;
  629.             case SOUTH : ny = y + 1; break;
  630.             case EAST  : nx = x + 1; break;
  631.             case WEST  : nx = x - 1; break;
  632.             }
  633.         od = otherDir(nd);
  634.         nl = level;
  635.  
  636.         /** get some level we can go onto from here **/
  637.         if (!(link=linkable(x, y, level, nd, nx, ny, nl, od))) {   /* stay on the same level if we can... */
  638.            if (level > MinLevel) {    /* can't stay on this level so go down */
  639.                nl = level - 1;
  640.                link = linkable(x, y, level, nd, nx, ny, nl, od); /* if we can't go down, go up */
  641.                }
  642.            if (!link && (level < MaxLevel - 1)) {
  643.                nl = level + 1;   /* can't go down or level, so try up */
  644.                link = linkable(x, y, level, nd, nx, ny, nl, od);
  645.                }
  646.            }
  647.  
  648.         if (link) {
  649.             board[x ][y ][level].conlev[nd] = nl;
  650.             board[nx][ny][nl   ].conlev[od] = level;
  651.             render( x, y);
  652.             render(nx,ny);
  653.             moves++;
  654.             /* sometimes fork, putting our old coordinates in a free end slot. */
  655.             if (firstfree != LASTEND) {  /* make sure there IS a free end slot */
  656.                 if (RangeRand(10L) < 4) {
  657.                    ends[firstfree].x = x;
  658.                    ends[firstfree].y = y;
  659.                    ends[firstfree].z = level;
  660.                    ends[firstfree].prevdir = nd;
  661.                    firstfree = ends[firstfree].nextfree;
  662.                    }
  663.                 }
  664.             ends[curend].x = x     = nx;
  665.             ends[curend].y = y     = ny;
  666.             ends[curend].z = level = nl;
  667.             }
  668.         }
  669.     /* if we got this far and made no moves, we are probably on dead end and */
  670.     /* should put this ends[] on the free list. */
  671.     if (moves == 0) {
  672.           ends[curend].x = FREEEND;
  673.           ends[curend].nextfree = firstfree;
  674.           firstfree = curend;
  675.           }
  676.     return(moves);
  677.     }
  678. int xties, xlength;
  679. int makepath()
  680. {   int i, endsfound, totalsquares;
  681.  
  682.     initends();   /* This sets the table of path ends to all zeros */
  683.  
  684.     /*   ends[] is a list of the ends of paths which we can add more paths */
  685.     /*   onto.  Note that they don't really have to be an end; they can be */
  686.     /*   in the middle of a path as well.   Not all of them are in use as */
  687.     /*   an end all the time.  Some are in a linked list of "free" ends, */
  688.     /*   available for use if we want to add an end.  Free (unused) array */
  689.     /*   elements are marked by a .x == FREEEND.  Ends get taken out of active */
  690.     /*   service and put on the free list when the path generator "extendend()" */
  691.     /*   fails to extend the path from that end.  When no active ends remain, */
  692.     /*   maze generation terminates. */
  693.  
  694.     /*  p.s.: I don't particularly care for the mazes that are generated by */
  695.     /*  this method, nor am I particularly fond of the method.  I would like */
  696.     /*  to hear of other strategies for maze generation. */
  697.  
  698.  
  699.     ends[firstfree].x = 1 + RangeRand((LONG)BoardMaxX - 1);
  700.     ends[firstfree].y = 1 + RangeRand((LONG)BoardMaxY - 1);
  701.     ends[firstfree].z = 0;
  702.     ends[firstfree].prevdir = RangeRand((LONG)DIRECTIONS);
  703.     firstfree = ends[firstfree].nextfree;
  704.  
  705.     totalsquares = 0;
  706.     do {
  707.          endsfound = 0;
  708.          for (i=0; i< MaxEnds; i++) {
  709.              if (ends[i].x != FREEEND) {
  710.                     endsfound++;
  711.                     totalsquares += extendend(i,xties,xlength);
  712.                     }
  713.              }
  714.          } while (endsfound);
  715.     return( totalsquares );
  716.     }
  717.  
  718. pickends()
  719. {   int x, y, z, i;
  720.  
  721.     /* Find then end of a path on the bottom level. */
  722.     do {
  723.         StartX = 1 + RangeRand( (LONG) BoardMaxX-1L );
  724.         StartY = 1 + RangeRand( (LONG) BoardMaxY-1L );
  725.         StartLevel =  MinLevel;
  726.         } while (references( StartX, StartY, StartLevel) != 1);
  727.  
  728.     /* The reason for doing this 4 times is to make the ends as far apart as */
  729.     /* possible.  If our first choice is close to the top of the tree, it */
  730.     /* can't be very far away from every leaf node.  The way this works is: */
  731.     /*     1  Find a leaf node. */
  732.     /*     2  Find the leaf farthest from the one found in step 1. */
  733.     /*     3  Find the leaf farthest from the one found in step 2. ... */
  734.     /* Eventually you should end up with two good choices for the ends. */
  735.  
  736.     for (i=0; i<4; i++) {
  737.         for (x=1; x<BoardMaxX; x++)
  738.             for (y=1; y<BoardMaxY; y++)
  739.                 for (z=MinLevel; z<MaxLevel; z++)
  740.                     board[x][y][z].compass = NOWHERE;
  741.  
  742.         EndX = StartX;   EndY = StartY;    EndLevel = StartLevel;
  743.         board[ EndX ][ EndY ][ EndLevel ].compass = SOLVED;
  744.         topscore = 0;
  745.         buildreturnmap(EndX, EndY, EndLevel, 0);
  746.         }
  747.  
  748.     board[ StartX ][ StartY ][ StartLevel ].inpath = FIRSTPIECE;
  749.     board[ EndX   ][ EndY   ][ EndLevel   ].inpath = LASTPIECE;
  750.     CurX     = StartX;
  751.     CurY     = StartY;
  752.     CurLevel = StartLevel;
  753.     NewX     = CurX;
  754.     NewY     = CurY;
  755.     NewLevel = CurLevel;
  756.     render( StartX, StartY);
  757.     render( EndX,   EndY);
  758.     }
  759.  
  760. init1( l )
  761. int l;
  762. {
  763.   int i, x, y, level, dir;
  764.   static int Xsize[] = { 54, 16, 24, 34 }; /* Square widths  */
  765.   static int Xsqrs[] = { 10, 34, 22, 16 }; /* No. of squares */
  766.  
  767.   static int Ysize[] = { 26, 10, 14, 18 }; /* Square heights */
  768.   static int Ysqrs[] = {  6, 16, 11,  9 }; /* No. of squares */
  769.  
  770.      /* Reset the entire board, including a strip of squares around */
  771.      /* the edges that we never go into. */
  772.   for(x=0; x <= BOARDMAXX; x++)
  773.      for(y=0; y <= BOARDMAXY; y++)
  774.         for(level=0; level < MAXLEVELS; level++) {
  775.            board[x][y][level].inpath   =  NOTTRAVERSED;
  776.            board[x][y][level].compass  =  NOWHERE;
  777.            for(dir=0; dir < DIRECTIONS; dir++)
  778.               board[x][y][level].conlev[dir] =  NOWHERE;
  779.            }
  780.  
  781.   SetAPen( rp, 0L);
  782.   SetBPen( rp, 0L);
  783.   RectFill( rp, 4L, 11L, (LONG)windowW-4, (LONG)windowH-2);
  784.  
  785.   MaxLevel = l;   /* l==0 is a special-case first-time super-easy 1-level */
  786.                   /* maze.  It generates fast so we can get to the menus. */
  787.  
  788.   BoardMaxX   = Xsqrs[ MaxLevel ] + 1;
  789.   SquareXsize = Xsize[ MaxLevel ];
  790.   BoardMaxY   = Ysqrs[ MaxLevel ] + 1;
  791.   SquareYsize = Ysize[ MaxLevel ];
  792.  
  793.   if (MaxLevel == 0)
  794.      MaxLevel = 1;
  795.  
  796.   makepath();
  797.  
  798.   pickends();
  799.   setHint( (int)FALSE );
  800.   }
  801.  
  802. int autosolve()
  803. {   int newDir;
  804.     square *oldSq, *newSq;
  805.  
  806.     while ( (newDir = board[CurX][CurY][CurLevel].compass) != SOLVED ) {
  807.           NewX = CurX;
  808.           NewY = CurY;
  809.           NewLevel = board[CurX][CurY][CurLevel].conlev[newDir];
  810.           switch (newDir) {
  811.               case NORTH : NewY--; break;
  812.               case SOUTH : NewY++; break;
  813.               case EAST  : NewX++; break;
  814.               case WEST  : NewX--; break;
  815.               }
  816.  
  817.           oldSq = &board[ CurX ][ CurY ][ CurLevel];
  818.           newSq = &board[ NewX ][ NewY ][ NewLevel];
  819.  
  820.           oldSq->inpath = TRAVERSED;
  821.  
  822.           switch (newSq->inpath) {
  823.               case TRAVERSED  :
  824.               case FIRSTPIECE : /* We must have backed up to get here, so...*/
  825.                           oldSq->inpath = NOTTRAVERSED; break;
  826.               default         : ;
  827.               }
  828.  
  829.           /* set the inpath flag of the new square in any case */
  830.           newSq->inpath = CURPIECE;
  831.  
  832.           /* Also, just in case we backed up to the starting square */
  833.           /*   or from the ending square.... */
  834.           board[StartX][StartY][StartLevel].inpath = FIRSTPIECE;
  835.           board[EndX  ][EndY  ][EndLevel  ].inpath = LASTPIECE;
  836.  
  837.           /* Now draw both squares */
  838.           render(CurX,CurY);
  839.           render(NewX,NewY);
  840.  
  841.           /* Now new becomes current */
  842.           CurX = NewX;
  843.           CurY = NewY;
  844.           CurLevel = NewLevel;
  845.  
  846.           }
  847.     }
  848.  
  849.  
  850.  
  851. int mousewatch()
  852. {   static int canmove = FALSE;
  853.     int x, y, newDir;
  854.  
  855.     x = ((MouseX) -  4)/SquareXsize + 1;
  856.     y = ((MouseY) - 11)/SquareYsize + 1;
  857.  
  858.     x = x * SIGN(MouseX);
  859.     y = y * SIGN(MouseY);
  860.  
  861.     if (x == CurX && y == CurY) {
  862.         canmove = TRUE;
  863.         }
  864.  
  865.     newDir = NOWHERE;
  866.     if (x == CurX) {
  867.          if (y == CurY + 1 && y < BoardMaxY)
  868.                newDir = SOUTH;
  869.             else if (y == CurY - 1 && y > 0)
  870.                       newDir = NORTH;
  871.          }
  872.       else
  873.          if (y == CurY) {
  874.                if (x == CurX + 1 && x < BoardMaxX)
  875.                      newDir = EAST;
  876.                   else if (x == CurX - 1 && x > 0)
  877.                            newDir = WEST;
  878.              }
  879.  
  880.     if (newDir == NOWHERE ) {
  881.          canmove = FALSE;
  882.          return(NOWHERE);
  883.          }
  884.  
  885.     NewLevel = board[CurX][CurY][CurLevel].conlev[newDir];
  886.  
  887.     if (NewLevel == NOWHERE) {
  888.        canmove = FALSE;
  889.        return(NOWHERE);
  890.        }
  891.     NewX = x;
  892.     NewY = y;
  893.     return(newDir);
  894.     }
  895.  
  896. int trymove() /* returns TRUE if we won, FALSE if we didn't win. */
  897. {   int newDir;
  898.     square *oldSq, *newSq;
  899.  
  900.     newDir = mousewatch();
  901.     if (newDir == NOWHERE)
  902.        return(board[CurX][CurY][CurLevel].compass == SOLVED);
  903.  
  904.     oldSq = &board[ CurX ][ CurY ][ CurLevel];
  905.     newSq = &board[ NewX ][ NewY ][ NewLevel];
  906.  
  907.     oldSq->inpath = TRAVERSED;
  908.     switch (newSq->inpath) {
  909.         case TRAVERSED  :
  910.         case FIRSTPIECE : /* We must have backed up to get here, so...*/
  911.                           oldSq->inpath = NOTTRAVERSED; break;
  912.         default         : ;
  913.         }
  914.  
  915.     /* set the inpath flag of the new square in any case */
  916.     newSq->inpath = CURPIECE;
  917.  
  918.     /* Also, just in case we backed up from the starting square */
  919.     /*   or the ending square.... */
  920.     board[StartX][StartY][StartLevel].inpath = FIRSTPIECE;
  921.     board[EndX  ][EndY  ][EndLevel  ].inpath = LASTPIECE;
  922.  
  923.     /* Now draw both squares */
  924.     render(CurX,CurY);
  925.     render(NewX,NewY);
  926.  
  927.     /* Now new becomes current */
  928.     CurX = NewX;
  929.     CurY = NewY;
  930.     CurLevel = NewLevel;
  931.  
  932.     /* ?did we win? */
  933.  
  934.     setHint((int)(newSq->compass == SOLVED));
  935.  
  936.     if (newSq->compass == SOLVED) {
  937.           cycle( 3 );
  938.           return( (int)TRUE );
  939.           }
  940.     return( (int)FALSE);
  941.     }
  942.  
  943. HandleMenu(menucode)
  944. USHORT menucode;
  945. {
  946.     ModifyIDCMP( window, (ULONG) MIDCMP2 );
  947.  
  948.     switch ( ITEMNUM( menucode) ) {
  949.         case 0 : about()   ;  break;
  950.         case 1 : init1( 1 );  break;
  951.         case 2 : init1( 2 );  break;
  952.         case 3 : init1( 3 );  break;
  953.         case 4 : autosolve(); break;
  954.         }
  955.  
  956.     ModifyIDCMP( window, (ULONG) MIDCMP1 );
  957.     }
  958.  
  959.  
  960. EventLoop()
  961. {
  962.   struct IntuiMessage *mesg;
  963.   ULONG  class, code, mousemoved, closewindow, menupick;
  964.   USHORT menucode;
  965.  
  966.   closewindow = FALSE;
  967.   ModifyIDCMP( window, (ULONG) MIDCMP1 );
  968.  
  969.   do {
  970.        mousemoved = FALSE;
  971.        menupick   = FALSE;
  972.  
  973.        Wait( (LONG) 1 << window -> UserPort -> mp_SigBit);
  974.  
  975.        while ((mesg=(struct IntuiMessage *)GetMsg(window->UserPort))) {
  976.              class = mesg->Class;
  977.              code  = mesg->Code;
  978.              MouseX = mesg->MouseX;
  979.              MouseY = mesg->MouseY;
  980.              ReplyMsg(mesg);
  981.              if (class == MOUSEMOVE) mousemoved = TRUE;
  982.              if (class == CLOSEWINDOW) closewindow = TRUE;
  983.              if (class == MOUSEBUTTONS && code == SELECTDOWN)
  984.                 setHint( (int) TRUE);
  985.              if (class == MENUPICK) {
  986.                 menupick = TRUE;
  987.                 menucode = code;
  988.                 }
  989.              }
  990.        if (mousemoved) trymove();
  991.        if (menupick)   HandleMenu(menucode);
  992.        } while (closewindow == FALSE);
  993.   }
  994.  
  995. struct TextFont *textfont = NULL;
  996.  
  997. openstuff()
  998. {   ULONG Seconds, Micros;
  999.  
  1000.     IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0L);
  1001.     if (IntuitionBase == NULL) {closestuff(); exit(0);}
  1002.  
  1003.     GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0L);
  1004.     if (GfxBase == NULL) {closestuff(); exit(0); }
  1005.  
  1006.     /* My <functions.h> shows OpenFont() returns a *Font. */
  1007.     /* I think that is an error! */
  1008.  
  1009.     textfont = (struct TextFont *)OpenFont( &font );
  1010.     if (textfont==NULL) { closestuff(); exit(0); }
  1011.  
  1012.     if (( window=(struct Window *)OpenWindow(&nw))==NULL) {
  1013.        closestuff();
  1014.        exit(0);
  1015.        }
  1016.     if (SetFont(window->RPort,textfont)==0) { closestuff(); exit(0); }
  1017.  
  1018.     SetWindowTitles(window,(UBYTE *)" TML's AmigaMaze     ver. 1.2 ",
  1019.             (UBYTE *)"TML's AmigaMaze       First distributed on JUMPDISK.");
  1020.  
  1021.     SetMenuStrip( window, &menu);
  1022.  
  1023.     /*  This next bit is a cludge because I can't seem to get */
  1024.     /*                 extern ULONG RangeSeed; */
  1025.     /*      to work.  Anyone got any ideas? */
  1026.     /*  LATER: It turns out that the RangeSeed is not public */
  1027.     /*      in the Manx sources.  Hope they fix this someday. */
  1028.     CurrentTime(&Seconds,&Micros);
  1029.     Micros = Micros & (ULONG) 0x0fff;
  1030.     for (Seconds=0; Seconds < Micros; Seconds++)
  1031.          RangeRand(4L);
  1032.     }
  1033.  
  1034.  
  1035. closestuff()
  1036. {   int i;
  1037.     for(i=0; i<2; i++) {
  1038.        if (tmpBM.Planes[i])
  1039.             FreeRaster(tmpBM.Planes[i],tmpBMxy,tmpBMxy);
  1040.        }
  1041.     if (window) {
  1042.         ClearMenuStrip( window );
  1043.         CloseWindow(window);
  1044.         }
  1045.     if (textfont) CloseFont(textfont);
  1046.     if (GfxBase) CloseLibrary(GfxBase);
  1047.     if (IntuitionBase) CloseLibrary(IntuitionBase);
  1048.     }
  1049.  
  1050. main(/*argc,argv*/)
  1051. /* int argc; */
  1052. /* char *argv[]; */
  1053. {   UWORD areabuffer[250], areabuffer2[250];
  1054.     struct TmpRas   tmpras,   tmprasB;
  1055.     struct AreaInfo areainfo, areainfo2;
  1056.     PLANEPTR plane,planeB;
  1057.     int i;
  1058.  
  1059.     /* if (argc > 1) */
  1060.     /*     xties = atoi(argv[1]); */
  1061.     /* if (xties < 1 || xties > 100) */
  1062.         xties = 30;
  1063.  
  1064.     /* if (argc > 2) */
  1065.     /*     xlength = atoi(argv[2]); */
  1066.     /* if (xlength < 1 || xlength > 100) */
  1067.         xlength = 9;
  1068.  
  1069.     /* if (argc > 3) */
  1070.     /*     MaxEnds = atoi(argv[3]); */
  1071.     /* if (MaxEnds < 2 || MaxEnds > MAXENDS) */
  1072.         MaxEnds = MAXENDS;
  1073.  
  1074.     MaxLevel = 1;
  1075.     MinLevel = 0;
  1076.     openstuff();
  1077.     InitBitMap( &tmpBM, 2L, tmpBMxy, tmpBMxy );
  1078.     for (i=0; i<2; i++) {
  1079.          if ((tmpBM.Planes[i] = (PLANEPTR)AllocRaster(tmpBMxy,tmpBMxy))==NULL) {
  1080.              closestuff();
  1081.              exit(0);
  1082.              }
  1083.          }
  1084.     rp = window->RPort;
  1085.     if ((plane = AllocRaster(windowW,windowH))==NULL) {
  1086.         closestuff();
  1087.         exit(0);
  1088.         }
  1089.     if ((planeB = AllocRaster(tmpBMxy,tmpBMxy))==NULL) {
  1090.         FreeRaster(plane, windowW,windowH);
  1091.         closestuff();
  1092.         exit(0);
  1093.         }
  1094.     InitArea(&areainfo, areabuffer, 90L);
  1095.     InitArea(&areainfo2,areabuffer2,90L);
  1096.  
  1097.     InitTmpRas(&tmpras, plane, RASSIZE( windowW, windowH));
  1098.     InitTmpRas(&tmprasB,planeB,RASSIZE( tmpBMxy, tmpBMxy));
  1099.  
  1100.     rp->AreaInfo    = &areainfo;
  1101.     rp->TmpRas      = &tmpras;
  1102.  
  1103.     InitRastPort( tmpRP );
  1104.     if (SetFont(tmpRP,textfont)==0) { closestuff(); exit(0); }
  1105.     tmpRP->BitMap = &tmpBM;
  1106.     tmpRP->AreaInfo = &areainfo2;
  1107.     tmpRP->TmpRas   = &tmprasB;
  1108.  
  1109.     init1(0); /* Zero is a special case.  Super simple fast easy maze
  1110.                  so the user can get to the menus w/o waiting so long. */
  1111.     ModifyIDCMP( window, (ULONG) MIDCMP2 );
  1112.     about();
  1113.     EventLoop();
  1114.     FreeRaster(plane, windowW, windowH);
  1115.     FreeRaster(planeB,tmpBMxy, tmpBMxy);
  1116.     closestuff();
  1117.     }
  1118.  
  1119. /* Save some code space by stubbing _wb_parse() and _cli_parse(). */
  1120. void _wb_parse()
  1121. { }
  1122.  
  1123. void _cli_parse()
  1124. { }
  1125.  
  1126.